﻿using Axiom.CodeManifold.Framework;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Serialization;

namespace Axiom.CodeManifold.WPF.ViewModels
{
    public class ServiceManifoldViewModel : ViewModelBase
    {
        #region field and properties
        private bool _appGatewayProject;
        private bool _appProxyProject;
        private bool _createRepositoryMethod;
        private string _newMethodComment;
        private string _newMethodName;
        private string _newMethodParameters;
        private string _newMethodReturnType;
        private bool _newMethodAddTests;

        public bool NewMethodAddTests
        {
            get
            {
                return _newMethodAddTests;
            }
            set
            {
                if (_newMethodAddTests == value)
                {
                    return;
                }
                _newMethodAddTests = value;
                NotifyPropertyChanged("NewMethodAddTests");
            }
        }

        private CodePropertyEx _newMethodSelectedRepository;

        private bool _repositoryAccessProject;

        private MethodDefinition _selectedMethod;

        private ServiceDefinition _selectedServiceInterface;

        protected ObservableCollection<ServiceDefinition> _serviceInterfaces = new ObservableCollection<ServiceDefinition>();

        private ObservableCollection<MethodDefinition> _serviceMethods = new ObservableCollection<MethodDefinition>();

        private CodeModelViewModel _settings = new CodeModelViewModel();

        private bool _svcHostProject;

        private bool _svcImplementationProject;

        private bool _svcInterfaceProject;

        private static Euclid Euclid = new Euclid();

        public ServiceManifoldViewModel()
        {
            Refresh();
        }

        public AddMethodRequest AddMethodRequest
        {
            get
            {
                AddMethodRequest request = new AddMethodRequest()
                {
                    ServiceName = SelectedServiceInterface.Name,
                    Method = new MethodDefinition()
                    {
                        Name = NewMethodName,
                        ReturnType = NewMethodReturnType,
                        Comment = NewMethodComment,
                    },
                    SelectedRepository = SelectedRepository,
                };

                request.Method.SetParameters(NewMethodParameters);

                return request;
            }
        }

        public bool AppGatewayProject
        {
            get
            {
                return _appGatewayProject;
            }
            set
            {
                if (_appGatewayProject == value) { return; }
                _appGatewayProject = value;
                NotifyPropertyChanged("AppGatewayProject");
            }
        }

        public bool AppProxyProject
        {
            get
            {
                return _appProxyProject;
            }
            set
            {
                if (_appProxyProject == value) { return; }
                _appProxyProject = value;
                NotifyPropertyChanged("AppProxyProject");
            }
        }

        public bool CreateRepositoryMethod
        {
            get
            {
                return _createRepositoryMethod;
            }
            set
            {
                if (_createRepositoryMethod == value)
                {
                    return;
                }
                _createRepositoryMethod = value;
                NotifyPropertyChanged("CreateRepositoryMethod");
            }
        }

		//public bool IsDisconnected
		//{
		//	get
		//	{
		//		return !Euclid.IsAttached;
		//	}
		//}

        public string NewMethodComment
        {
            get
            {
                return _newMethodComment;
            }
            set
            {
                if (_newMethodComment == value)
                {
                    return;
                }
                _newMethodComment = value;
                NotifyPropertyChanged("NewMethodComment");
            }
        }

        public string NewMethodName
        {
            get
            {
                return _newMethodName;
            }
            set
            {
                if (_newMethodName == value)
                {
                    return;
                }
                _newMethodName = value;
                NotifyPropertyChanged("NewMethodName");
            }
        }

        public string NewMethodParameters
        {
            get
            {
                return _newMethodParameters;
            }
            set
            {
                if (_newMethodParameters == value)
                {
                    return;
                }
                _newMethodParameters = value;
                NotifyPropertyChanged("NewMethodParameters");
            }
        }

        public string NewMethodReturnType
        {
            get
            {
                return _newMethodReturnType;
            }
            set
            {
                if (_newMethodReturnType == value)
                {
                    return;
                }
                _newMethodReturnType = value;
                NotifyPropertyChanged("NewMethodReturnType");
            }
        }

        public bool RepositoryAccessProject
        {
            get
            {
                return _repositoryAccessProject;
            }
            set
            {
                if (_repositoryAccessProject == value) { return; }
                _repositoryAccessProject = value;
                NotifyPropertyChanged("RepositoryAccessProject");
            }
        }

        public CodePropertyEx SelectedRepository
        {
            get
            {
                return _newMethodSelectedRepository;
            }
            set
            {
                if (_newMethodSelectedRepository == value)
                {
                    return;
                }
                _newMethodSelectedRepository = value;
                NotifyPropertyChanged("SelectedRepository");
            }
        }

        public ServiceDefinition SelectedServiceInterface
        {
            get
            {
                return _selectedServiceInterface;
            }
            set
            {
                if (_selectedServiceInterface == value)
                {
                    return;
                }
                _selectedServiceInterface = value;
                NotifyPropertyChanged("SelectedServiceInterface");
            }
        }

        public MethodDefinition SelectedServiceMethod
        {
            get
            {
                if (_selectedMethod == null)
                {
                    _selectedMethod = new MethodDefinition()
                    {
                        Name = this.NewMethodName,
                        ReturnType = this.NewMethodReturnType,
                        Comment = this.NewMethodComment,
                    };
                }
                return _selectedMethod;
            }
            set
            {
                if (object.ReferenceEquals(_selectedMethod, value))
                {
                    return;
                }
                _selectedMethod = value;
                NotifyPropertyChanged("SelectedMethod");

                if (value == null)
                {
                    return;
                }
                NewMethodName = value.Name;
                NewMethodComment = value.Comment;
                NewMethodParameters = value.ArgumentSignature;
                NewMethodReturnType = value.ReturnType;
            }
        }

        public ObservableCollection<ServiceDefinition> ServiceInterfaces
        {
            get { return _serviceInterfaces; }
        }

        public ObservableCollection<MethodDefinition> ServiceMethods
        {
            get { return _serviceMethods; }
        }

		public EnvDTE.DTE ServiceSolution { get { return Euclid.Ide; } }

        public CodeModelViewModel Settings { get { return _settings; } }

        public EnvDTE.DTE SiteSolution { get { return Euclid.Ide; } }

        public bool SvcHostProject
        {
            get
            {
                return _svcHostProject;
            }
            set
            {
                if (_svcHostProject == value) { return; }
                _svcHostProject = value;
                NotifyPropertyChanged("SvcHostProject");
            }
        }

        public bool SvcImplementationProject
        {
            get
            {
                return _svcImplementationProject;
            }
            set
            {
                if (_svcImplementationProject == value) { return; }
                _svcImplementationProject = value;
                NotifyPropertyChanged("SvcImplementationProject");
            }
        }

        public bool SvcInterfaceProject
        {
            get
            {
                return _svcInterfaceProject;
            }
            set
            {
                if (_svcInterfaceProject == value) { return; }
                _svcInterfaceProject = value;
                NotifyPropertyChanged("SvcInterfaceProject");
            }
        }
        #endregion field and properties

        public async void AddNewMethod()
        {
            if (IsBusy)
            {
                Status = "please wait...";
                return;
            }

            IsBusy = true;
            Status = "Adding Method";
            UpdateConnectionStatus();

            string status = string.Empty;
            AddMethodRequest request = null;
            await Task.Run(() =>
            {
                try
                {
                    if (SiteSolution.UndoContext.IsOpen)
                    {
                        SiteSolution.UndoContext.Close();
                    }
                    if (ServiceSolution.UndoContext.IsOpen)
                    {
                        ServiceSolution.UndoContext.Close();
                    }
                    SiteSolution.UndoContext.Open("AddUpdateMethod_UndoContext");
                    ServiceSolution.UndoContext.Open("AddUpdateMethod_UndoContext");

                    request = CreateMethodRequest();
                    if (request.Method.Memento != null && !request.Method.IsModified)
                    {
                        status = "No action required";
                    }
					//if (request.Method.Memento != null && request.Method.IsModified)
					//{
					//	Euclid.UpdateMethod(request);
					//	status = "Successful - service method updated";
					//}
					//else
					//{
					//	Euclid.AddMethod(request);
					//	status = "Successful - service method created";
					//	request.Success = true;
					//}
                }
                catch (System.Exception ex)
                {
                    Undo();
                    status = "No Change: " + new StringReader(ex.Message).ReadLine();
                }

                if (Axiom.CodeManifold.Properties.Settings.Default.UseUndoContext)
                {
                    if (SiteSolution.UndoContext.IsOpen)
                    {
                        SiteSolution.UndoContext.Close();
                    }
                    if (ServiceSolution.UndoContext.IsOpen)
                    {
                        ServiceSolution.UndoContext.Close();
                    }
                }
            });

            if (request.Success && !SelectedServiceInterface.Methods.Any(a => a.Signature.CompareTo(request.Method.Signature) == 0))
            {
                request.Method.Store();
                SelectedServiceInterface.Methods.Add(request.Method);
            }

            IsBusy = false;
            Status = status;

            App.Current.MainWindow.Activate();
        }

        public AddMethodRequest CreateMethodRequest()
        {
            if (!string.IsNullOrWhiteSpace(SelectedServiceMethod.Aggregate) && SelectedServiceMethod.Aggregate != SelectedServiceInterface.Aggregate)
            {
                throw new ServiceMethodMismatchException(string.Format("Method/Service Mismatch: this method belongs to the {0} Service", SelectedServiceMethod.Aggregate));
            }

            var request = new AddMethodRequest()
            {
                AddTests = NewMethodAddTests,
                ServiceName = this.SelectedServiceInterface.Name,
                SelectedRepository = this.SelectedRepository,
                Method = new MethodDefinition()
                {
                    Aggregate = this.SelectedServiceMethod.Aggregate,
                    Namespace = this.SelectedServiceMethod.Namespace,
                    Name = this.NewMethodName,
                    ReturnType = this.NewMethodReturnType,
                    Comment = this.NewMethodComment,
                    Memento = this.SelectedServiceMethod.Memento,
                },
            };

            request.Method.SetParameters(NewMethodParameters);

            return request;
        }

        public async void LaunchSolutions()
        {
            if (IsBusy)
            {
                Status = "please wait...";
                return;
            }

            if (Euclid.IsReady)
            {
                Status = "Solutions are already open";
                return;
            }

            try
            {
                IsBusy = true;
                Euclid.LaunchSolutionInstances();
                Status = "Starting Visual Studio - polling";

                await Task.Run(() =>
                {
                    while (!Euclid.IsReady)
                    {
                        System.Threading.Thread.Sleep(200);
                    }
                });

                Refresh();
            }
            catch (Exception ex)
            {
                Status = new StringReader(ex.Message).ReadLine();
            }
            UpdateConnectionStatus();
            IsBusy = false;
        }

        public virtual async void Refresh()
        {
            UpdateConnectionStatus();
            if (!Euclid.IsReady)
            {
                Status = "Solution instances not currently running";
                return;
            }

            List<ServiceDefinition> services = null;

            IsBusy = true;
            Status = "Loading Service Definitions";

            try
            {
                await Task.Run(() =>
                {
					Euclid.Attach(ReferenceData.SolutionFullName);

                    NotifyPropertyChanged("ServiceSolution");
                    NotifyPropertyChanged("SiteSolution");

					//if (!Euclid.IsAttached)
					//{
					//	IsBusy = false;
					//	Status = "Cannot Attach to Solutions - Attempting to open Solution Instances";
					//	return;
					//}

					//services = Euclid.GetServiceDefinitions();
					//services.Sort((a, b) => a.Name.CompareTo(b.Name));
                });

                ServiceInterfaces.Clear();

                if (services == null)
                {
                    IsBusy = false;
                    Status = "Euclid Solutions Not Available.";
                    return;
                }

                var methods = new List<MethodDefinition>();
                foreach (var service in services)
                {
                    ServiceInterfaces.Add(service);
                    methods.AddRange(service.Methods);
                }

                ServiceMethods.Clear();
                methods.Sort((a, b) => a.Name.CompareTo(b.Name));
                foreach (var method in methods)
                {
                    ServiceMethods.Add(method);
                }

                SelectedServiceInterface = ServiceInterfaces.FirstOrDefault();
#if DEBUG
				SaveSampleData(services);
#endif
                Status = "Ready";
            }
            catch (AggregateException exs)
            {
                var ex = exs.InnerExceptions.FirstOrDefault();
                if (ex != null)
                {
                    Status = new StringReader(ex.Message).ReadLine();
                }
            }
            catch (Exception ex)
            {
                Status = new StringReader(ex.Message).ReadLine();
            }
            IsBusy = false;
            UpdateConnectionStatus();
        }

        private static void SaveSampleData(List<ServiceDefinition> services)
        {
            using (FileStream fs = new FileStream("sample-data.xml", FileMode.Create, FileAccess.Write))
            {
                XmlSerializer xs = new XmlSerializer(typeof(List<ServiceDefinition>));
                xs.Serialize(fs, services);
            }
        }

        public void SortMembers()
        {
			Euclid.SortMembers();
        }

        public void Undo()
        {
            try
            {
                if (SiteSolution != null && SiteSolution.UndoContext.IsOpen)
                {
                    SiteSolution.UndoContext.SetAborted();
                }
                if (ServiceSolution != null && ServiceSolution.UndoContext.IsOpen)
                {
                    ServiceSolution.UndoContext.SetAborted();
                }
            }
            catch (Exception ex)
            {
                Status = new StringReader(ex.Message).ReadLine();
            }
        }

        private void UpdateConnectionStatus()
        {
            try
            {
				//AppProxyProject = SiteSolution.ContainsProject(ReferenceData.ProxyProjectName);
				//RepositoryAccessProject = ServiceSolution.ContainsProject(ReferenceData.RepositoryProjectName);
				//AppGatewayProject = SiteSolution.ContainsProject(ReferenceData.GatewayProjectName);
				//SvcInterfaceProject = ServiceSolution.ContainsProject(ReferenceData.InterfaceProjectName);
				//SvcImplementationProject = ServiceSolution.ContainsProject(ReferenceData.ImplementationProjectName);
                NotifyPropertyChanged("IsDisconnected");
                NotifyPropertyChanged("ServiceSolution");
                NotifyPropertyChanged("SiteSolution");
            }
            catch (Exception ex)
            {
                Status = new StringReader(ex.Message).ReadLine();
            }
        }
    }
}
